
from saas.crypto import *
from saas.const import *
from saas.misc import *
from saas.db import sqlstr



##################################################################################################################################
##
##  AUTH
##
def func_authenticate( inst , username , pwhash  ):
	## return with:  ( result , pw_expiry )

	## verify the password hash and username looks valid
	if not validuser( username ) or not valid512hash( pwhash ):
		return ( SAAS_OP_MALFORMED_REQUEST , 0 )

	## lookup the user and the password hash.
	sql = 'SELECT uid, pass_expire, password_hash FROM users WHERE username = \'%s\'' % ( username )
	result = inst.db.query( sql )
	if len( result ) != 1:
		return ( SAAS_OP_USER_NOT_FOUND , 0 )
	uid       = result[0][0]
	pw_expiry = result[0][1]
	pw_hash   = result[0][2]

	if pw_hash.lower() != pwhash.encode('hex').lower():
		return ( SAAS_OP_INVALID_PASSWORD , 0 )

	## now we look up to see if that UID is allowed on to that host.
#	if not func_user_allowed_on_host( username , hostname ):
#		return ( SAAS_OP_USER_UNAUTHORISED , 0 )

	return ( SAAS_OP_SUCCESS , 123456789 )




##################################################################################################################################
##
##  USER:  UID/USERNAME CONVERSION
##
def func_uid_to_username( inst , uid ):
	## return with:  ( result , username )

	if not is_numeric( uid ):
		return ( SAAS_OP_USER_NOT_FOUND , '' )

	sql = 'SELECT username FROM users WHERE uid = %d' % ( uid )
	dbresult = inst.db.query( sql )
	if len( dbresult ) != 1:
		return ( SAAS_OP_USER_NOT_FOUND , '' )

	return ( SAAS_OP_SUCCESS , dbresult[0][0] )

def func_username_to_uid( inst , username ):
	## return with:  ( result , uid )

	if not validuser ( username ):
		return ( SAAS_OP_INVALID_USERNAME , 0 )

	sql = 'SELECT uid FROM users WHERE username = \'%s\'' % ( sqlstr( username ) )
	dbresult = inst.db.query( sql )
	if len( dbresult ) != 1:
		return ( SAAS_OP_USER_NOT_FOUND , 0 )

	return ( SAAS_OP_SUCCESS , dbresult[0][0] )




##################################################################################################################################
##
##  GROUP:  GID/GROUPNAME CONVERSION
##
def func_gid_to_groupname( inst , gid ):
	## return with:  ( result , groupname )

	if not is_numeric( gid ):
		return ( SAAS_OP_INVALID_GID , '' )

	sql = 'SELECT groupname FROM groups WHERE gid = %d' % ( gid )
	dbresult = inst.db.query( sql )
	if len( dbresult ) != 1:
		return ( SAAS_OP_GROUP_NOT_FOUND , '' )

	return ( SAAS_OP_SUCCESS , dbresult[0][0] )

def func_groupname_to_gid( inst , groupname ):
	## return with:  ( result , gid )

	if not validuser ( groupname ):
		return ( SAAS_OP_INVALID_GROUPNAME , 0 )

	sql = 'SELECT gid FROM groups WHERE groupname = \'%s\'' % ( sqlstr( groupname ) )
	dbresult = inst.db.query( sql )
	if len( dbresult ) != 1:
		return ( SAAS_OP_GROUP_NOT_FOUND , 0 )

	return ( SAAS_OP_SUCCESS , dbresult[0][0] )




##################################################################################################################################
##
##  USER INFO
##
def func_user_info_by_uid( inst , uid ):
	if not is_numeric( uid ):
		return ( SAAS_OP_USER_NOT_FOUND , {} )

	user_info = {}
	sql = 'SELECT username, uid, gid, fullname, displayname, fname, lname, email, homedir, shell FROM users WHERE uid = %d' % ( uid )
	dbresult = inst.db.query( sql )
	if len( dbresult ) != 1:
		return ( SAAS_OP_USER_NOT_FOUND , {} )

	user_info['username']    = dbresult[0][0]
	user_info['uid']         = dbresult[0][1]
	user_info['gid']         = dbresult[0][2]
	user_info['fullname']    = dbresult[0][3]
	user_info['displayname'] = dbresult[0][4]
	user_info['fname']       = dbresult[0][5]
	user_info['lname']       = dbresult[0][6]
	user_info['email']       = dbresult[0][7]
	user_info['home_dir']    = dbresult[0][8]
	user_info['shell']       = dbresult[0][9]
	return ( SAAS_OP_SUCCESS , user_info )

##################################################################################################################################

def func_user_info_by_username( inst , username ):
	if not validuser( username ):
		return ( SAAS_OP_USER_NOT_FOUND , {} )

	user_info = {}
	sql = 'SELECT username, uid, gid, fullname, displayname, fname, lname, email, homedir, shell FROM users WHERE username = \'%s\'' % ( sqlstr( username ) )
	dbresult = inst.db.query( sql )
	if len( dbresult ) != 1:
		return ( SAAS_OP_USER_NOT_FOUND , {} )

	user_info['username']    = dbresult[0][0]
	user_info['uid']         = dbresult[0][1]
	user_info['gid']         = dbresult[0][2]
	user_info['fullname']    = dbresult[0][3]
	user_info['displayname'] = dbresult[0][4]
	user_info['fname']       = dbresult[0][5]
	user_info['lname']       = dbresult[0][6]
	user_info['email']       = dbresult[0][7]
	user_info['home_dir']    = dbresult[0][8]
	user_info['shell']       = dbresult[0][9]
	return ( SAAS_OP_SUCCESS , user_info )

##################################################################################################################################

def func_user_groups_by_uid( inst , uid ):
	## return with:  ( result , [ (gid1, groupname1), (gid2, groupname2) ] )

	if not is_numeric( uid ):
		return ( SAAS_OP_USER_NOT_FOUND , [] )

	groups = []

	## get the GID that they're a member of as well.
	sql = 'SELECT u.gid, g.groupname FROM users u JOIN groups g ON u.gid = g.gid WHERE u.uid = %d' % ( uid )
	dbresult = inst.db.query( sql )
	if len( dbresult ) != 1:
		return ( SAAS_OP_USER_NOT_FOUND , [] )
	groups.append( ( dbresult[0][0] , dbresult[0][1] ) )

	sql = 'SELECT g.gid, g.groupname FROM group_mem_users gmu JOIN groups g ON gmu.gid = g.gid WHERE gmu.uid = %d' % ( uid )
	dbresult = inst.db.query( sql )
	for dbrow in dbresult:
		groups.append( ( dbrow[0] , dbrow[1] ) )

	return ( SAAS_OP_SUCCESS , groups )

##################################################################################################################################

def func_user_groups_by_username( inst , username ):
	## return with:  ( result , [ (gid1, groupname1), (gid2, groupname2) ] )

	if not validuser( username ):
		return ( SAAS_OP_INVALID_USERNAME , [] )

	groups = []

	## get the GID that they're a member of as well.
	sql = 'SELECT u.gid, g.groupname FROM users u JOIN groups g ON u.gid = g.gid WHERE u.username = \'%s\'' % ( sqlstr( username ) )
	dbresult = inst.db.query( sql )
	if len( dbresult ) != 1:
		return ( SAAS_OP_USER_NOT_FOUND , [] )
	groups.append( ( dbresult[0][0] , dbresult[0][1] ) )

	sql = 'SELECT g.gid, g.groupname FROM users u JOIN group_mem_users gmu ON gmu.uid = u.uid JOIN groups g ON gmu.gid = g.gid WHERE u.username = \'%s\'' % ( sqlstr( username ) )
	dbresult = inst.db.query( sql )
	for dbrow in dbresult:
		groups.append( ( dbrow[0] , dbrow[1] ) )

	return ( SAAS_OP_SUCCESS , groups )

##################################################################################################################################

def func_user_all_groups_by_uid( inst , uid ):
	## return with:  ( result , [ (gid1, groupname1), (gid2, groupname2) ] )

	if not is_numeric( uid ):
		return ( SAAS_OP_USER_NOT_FOUND , [] )

	## pull down the groups this user is directly a member of.
	( result , groups ) = func_user_groups_by_uid( inst , uid )
	if result != SAAS_OP_SUCCESS:
		return result

	## start the groups list with the ones the user is directly a member of.
	new_gids = get_uids( groups )

	## keep track of what GIDs we've added (to prevent duplicates)
	gids = []

	## recurse through all groups that are related
	while 1:
		sql = 'SELECT g.gid, g.groupname FROM group_mem_groups gmg JOIN groups g ON gmg.gid = g.gid WHERE gmg.gid_mem IN ( ' + ','.join( new_gids ) + ' )'
		dbresult = inst.db.query( sql )
		if len( dbresult ) == 0:
			## there are no other groups related to this user. We have finished the recursion.
			break

		new_gids = []
		for dbrow in dbresult:
			if not dbrow[0] in gids:
				groups.append( ( dbrow[0] , dbrow[1] ) )
				new_gids.append( str( dbrow[0] ) )
				gids.append( dbrow[0] )

	return ( SAAS_OP_SUCCESS , groups )

##################################################################################################################################

def func_user_all_groups_by_username( inst , username ):
	## return with:  ( result , [ (gid1, groupname1), (gid2, groupname2) ] )

	if not validuser( username ):
		return ( SAAS_OP_USER_NOT_FOUND , [] )

	## pull down the groups this user is directly a member of.
	( result , groups ) = func_user_groups_by_username( inst , username )
	if result != SAAS_OP_SUCCESS:
		return result

	## start the groups list with the ones the user is directly a member of.
	new_gids = get_uids( groups )

	## keep track of what GIDs we've added (to prevent duplicates)
	gids = []

	## recurse through all groups that are related
	while 1:
		sql = 'SELECT g.gid, g.groupname FROM group_mem_groups gmg JOIN groups g ON gmg.gid = g.gid WHERE gmg.gid_mem IN ( ' + ','.join( new_gids ) + ' )'
		dbresult = inst.db.query( sql )
		if len( dbresult ) == 0:
			## there are no other groups related to this user. We have finished the recursion.
			break

		new_gids = []
		for dbrow in dbresult:
			if not dbrow[0] in gids:
				groups.append( ( dbrow[0] , dbrow[1] ) )
				new_gids.append( str( dbrow[0] ) )
				gids.append( dbrow[0] )

	return ( SAAS_OP_SUCCESS , groups )






##################################################################################################################################
##
##  GROUP INFO
##
def func_group_info_by_gid( inst , gid ):
	if not is_numeric( gid ):
		return ( SAAS_OP_INVALID_GID , {} )

	group_info = {}
	sql = 'SELECT groupname, gid FROM groups WHERE gid = %d' % ( gid )
	dbresult = inst.db.query( sql )
	if len( dbresult ) != 1:
		return ( SAAS_OP_GROUP_NOT_FOUND , {} )

	group_info['groupname']   = dbresult[0][0]
	group_info['gid']         = dbresult[0][1]
	return ( SAAS_OP_SUCCESS , group_info )

##################################################################################################################################

def func_group_info_by_groupname( inst , groupname ):
	if not validuser( groupname ):
		return ( SAAS_OP_INVALID_GID , {} )

	group_info = {}
	sql = 'SELECT groupname, gid FROM groups WHERE groupname = \'%s\'' % ( sqlstr( groupname ) )
	dbresult = inst.db.query( sql )
	if len( dbresult ) != 1:
		return ( SAAS_OP_GROUP_NOT_FOUND , {} )

	group_info['groupname']   = dbresult[0][0]
	group_info['gid']         = dbresult[0][1]
	return ( SAAS_OP_SUCCESS , group_info )

##################################################################################################################################

def func_group_members_by_gid( inst , gid ):
	## return with:  ( result , [ (type1, id1, name1), (type2, id2, name2) ] )

	if not is_numeric( gid ):
		return ( SAAS_OP_INVALID_GID , [] )

	members = []
	sql = 'SELECT u.uid, u.username FROM group_mem_users gmu JOIN users u ON gmu.uid = u.uid WHERE gmu.gid = %d' % ( gid )
	dbresult = inst.db.query( sql )
	for dbrow in dbresult:
		members.append( ( SAAS_GROUP_MEMBER_TYPE_USER , dbrow[0] , dbrow[1] ) )

	sql = 'SELECT g.gid, g.groupname FROM group_mem_groups gmg JOIN groups g ON gmg.gid_mem = g.gid WHERE gmg.gid = %d' % ( gid )
	dbresult = inst.db.query( sql )
	for dbrow in dbresult:
		members.append( ( SAAS_GROUP_MEMBER_TYPE_GROUP , dbrow[0] , dbrow[1] ) )

	return ( SAAS_OP_SUCCESS , members )

##################################################################################################################################

def func_group_members_by_groupname( inst , groupname ):
	## return with:  ( result , [ (type1, id1, name1), (type2, id2, name2) ] )

	if not validuser( groupname ):
		return ( SAAS_OP_GROUP_NOT_FOUND , [] )

	members = []
	sql = 'SELECT u.uid, u.username FROM users u JOIN group_mem_users gmu ON gmu.uid = u.uid JOIN groups g ON gmu.gid = g.gid WHERE g.groupname = \'%s\'' % ( sqlstr( groupname ) )
	dbresult = inst.db.query( sql )
	for dbrow in dbresult:
		members.append( ( SAAS_GROUP_MEMBER_TYPE_USER , dbrow[0] , dbrow[1] ) )

	sql = 'SELECT gm.gid, gm.groupname FROM groups g JOIN group_mem_groups gmg ON g.gid = gmg.gid JOIN groups gm ON gmg.gid_mem = gm.gid WHERE g.groupname = \'%s\'' % ( groupname )
	dbresult = inst.db.query( sql )
	for dbrow in dbresult:
		members.append( ( SAAS_GROUP_MEMBER_TYPE_GROUP , dbrow[0] , dbrow[1] ) )

	return ( SAAS_OP_SUCCESS , members )

##################################################################################################################################

def func_group_all_members_by_gid( inst , gid ):
	## return with:  ( result , [ (type1, id1, name1), (type2, id2, name2) ] )

	if not is_numeric( gid ):
		return ( SAAS_OP_INVALID_GID , [] )

	( result , members ) = func_group_members_by_gid( inst , gid )
	if result != SAAS_OP_SUCCESS:
		return result

	## start the groups list with the ones the groups are directly a member of.
	new_gids = get_gids( members , SAAS_GROUP_MEMBER_TYPE_GROUP )
	new_gids.append( str( gid ) )

	## keep track of what IDs we've added (to prevent duplicates)
	uids = []
	gids = []

	## recurse through all users and groups that are related
	members = []
	while 1:
		## grab all the users in the new_gids list
		sql = 'SELECT u.uid, u.username FROM group_mem_users gmu JOIN users u ON gmu.uid = u.uid WHERE gmu.gid IN ( ' + ','.join( new_gids ) + ' )'
		dbresult = inst.db.query( sql )
		for dbrow in dbresult:
			if not dbrow[0] in uids:
				members.append( ( SAAS_GROUP_MEMBER_TYPE_USER , dbrow[0] , dbrow[1] ) )
				uids.append( dbrow[0] )

		## now get a list of all the GIDs within this one, and recurse into them in the next iteration.
		sql = 'SELECT g.gid, g.groupname FROM group_mem_groups gmg JOIN groups g ON gmg.gid_mem = g.gid WHERE gmg.gid IN ( ' + ','.join( new_gids ) + ' )'
		dbresult = inst.db.query( sql )
		if len( dbresult ) == 0:
			## we've reached this because there's no further groups that are a member of this one.
			break

		new_gids = []
		for dbrow in dbresult:
			new_gids.append( str( dbrow[0] ) )
			if not dbrow[0] in gids:
				members.append( ( SAAS_GROUP_MEMBER_TYPE_GROUP , dbrow[0] , dbrow[1] ) )
				gids.append( dbrow[0] )

	return ( SAAS_OP_SUCCESS , members )

##################################################################################################################################

def func_group_all_members_by_groupname( inst , groupname ):
	## return with:  ( result , [ (type1, id1, name1), (type2, id2, name2) ] )

	if not validgroup( groupname ):
		return ( SAAS_OP_GROUP_NOT_FOUND , [] )

	( result , members ) = func_group_members_by_groupname( inst , groupname )
	if result != SAAS_OP_SUCCESS:
		return result

	## start the groups list with the ones the groups are directly a member of.
	new_gids = get_gids( members , SAAS_GROUP_MEMBER_TYPE_GROUP )

	( result , group_info ) = func_group_info_by_groupname( inst , groupname )
	if result != SAAS_OP_SUCCESS:
		return result
	new_gids.append( str( group_info['gid'] ) )

	## keep track of what IDs we've added (to prevent duplicates)
	uids = []
	gids = []

	## recurse through all users and groups that are related
	members = []
	while 1:
		## grab all the users in the new_gids list
		sql = 'SELECT u.uid, u.username FROM group_mem_users gmu JOIN users u ON gmu.uid = u.uid WHERE gmu.gid IN ( ' + ','.join( new_gids ) + ' )'
		dbresult = inst.db.query( sql )
		for dbrow in dbresult:
			if not dbrow[0] in uids:
				members.append( ( SAAS_GROUP_MEMBER_TYPE_USER , dbrow[0] , dbrow[1] ) )
				uids.append( dbrow[0] )

		## now get a list of all the GIDs within this one, and recurse into them in the next iteration.
		sql = 'SELECT g.gid, g.groupname FROM group_mem_groups gmg JOIN groups g ON gmg.gid_mem = g.gid WHERE gmg.gid IN ( ' + ','.join( new_gids ) + ' )'
		dbresult = inst.db.query( sql )
		if len( dbresult ) == 0:
			## we've reached this because there's no further groups that are a member of this one.
			break

		new_gids = []
		for dbrow in dbresult:
			new_gids.append( str( dbrow[0] ) )
			if not dbrow[0] in gids:
				members.append( ( SAAS_GROUP_MEMBER_TYPE_GROUP , dbrow[0] , dbrow[1] ) )
				gids.append( dbrow[0] )

	return ( SAAS_OP_SUCCESS , members )




def func_user_allowed_on_host( username , hostname ):
	pass





def get_uids( idlist ):
	## input:  [ (uid1, username1), (uid2, username2) ]
	## output: [ uid1, uid2, uid3 ]
	output = []
	for (id,name) in idlist:  output.append( str( id ) )
	return output


def get_gids( idlist , only=-1 ):
	## input:  [ (type1, gid1, groupname1), (type2, gid2, groupname2) ]
	## output: [ gid1, gid2, gid3 ]
	output = []
	for (mtype,id,name) in idlist:
		if only == mtype or only == -1:
			output.append( str( id ) )
	return output





def is_numeric( string ):
	try:
		string = int( string )
	except ValueError, TypeError:
		return False
	return True


